/* COPYRIGHT_CHUNFENG */
#include "cf_common.h"
#include "cf_std.h"
#include "cf_errno.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/file.h>
#ifdef _WIN32_

#else
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/wait.h>
#endif
#include "lcf_log.h"

int CFErrno;

inline void uint32_set_bits(uint32* reg, uint32 mask)
{
    *reg |= mask;
}

inline void uint32_clear_bits(uint32* reg, uint32 mask)
{
    *reg &= ~mask;
}

inline void uint32_modify_bits(uint32* reg, uint32 mask, uint32 value)
{
    uint32 tmp_reg = *reg;
    uint32_set_bits(&tmp_reg, mask&value);
    uint32_clear_bits(&tmp_reg, mask&(~value));
    *reg = tmp_reg;
}

int CFStrToInt(const char* str, int* value)
{
    if(1!=sscanf(str, "%i", value)){
        LCF_ERR("\n");
        return -1;
    }
    return 0;
}

int CFIntToString(char* str, int val)
{
    sprintf(str, "%i", val);
    return 0;
}

int CFIntToFile(const char* file, int val)
{
    char intStr[10];
    CFIntToString(intStr, val);
    return CFStringToFile(file, intStr);
}

long long cf_atoll_n(char* a, int n)
{
    char* buf = (char*)malloc(n+1);
    int res;
    strncpy(buf, a, n);
    buf[n] = '\0';
    res = atoll(buf);
    free(buf);
    return res;
}

const char* cf_lltoa(long long int num)
{
    static char a[10];
    long long int llnum = num;
    sprintf(a, "%lli", llnum);
    return a;
}

int cf_char_is_blank(char c)
{
	switch(c)
	{
		case ' ':
		case '\t':
		case '\r':
		case '\n':
			return TRUE;
	}
	return FALSE;
}

void cf_char_strip(char** start, char** end, int strip_start, int strip_end)
{
	int start_blank = FALSE, end_blank = FALSE;
	while(*end > *start){
		if(strip_start)
			start_blank = cf_char_is_blank(**start);
		if(strip_end)
			end_blank = cf_char_is_blank(**end);
		if(start_blank) (*start)++;
		if(end_blank) (*end)--;
		if(!start_blank && !end_blank)  break;
	}
}

char* strblank(const char* str)
{
	while(*str){
		if(cf_char_is_blank(*str)) return (char*)str;
		str++;
	}
	return NULL;
}

char* strnblank(const char* str)
{
	while(*str){
		if(!cf_char_is_blank(*str)) return (char*)str;
		str++;
	}
    return NULL;
}
#ifdef _WIN32_
#else
int cf_popen(const char*  args[], int* from_stdout, int* from_errout, int* to_stdin, int* pid_out)
{
	int stdout_pipe[2], err_pipe[2], stdin_pipe[2];
	int pid;
	if(from_stdout)
		pipe(stdout_pipe);
	if(from_errout)
		pipe(err_pipe);
	if(to_stdin)
		pipe(stdin_pipe);
	pid = fork();
	if(pid == 0){
		if(from_stdout)
			close(stdout_pipe[0]);
		if(from_errout)
			close(err_pipe[0]);
		close(stdin_pipe[1]);
		if(from_stdout)
			dup2(stdout_pipe[1], 1);
		if(from_errout)
			dup2(err_pipe[1], 2);
		if(to_stdin)
			dup2(stdin_pipe[0], 0);
		execvp(args[0], args);
		exit(0);
	}else if(pid > 0){
		if(from_stdout){
			close(stdout_pipe[1]);
			*from_stdout = stdout_pipe[0];
		}
		if(from_errout){
			close(err_pipe[1]);
			*from_errout = err_pipe[0];
		}
		if(to_stdin){
			close(stdin_pipe[0]);
			*to_stdin = stdin_pipe[1];
		}
		*pid_out=pid;
		return 0;
	}else{
		perror("fork failed\n");
	}
	return -1;
}

int cf_system(const char* args[], CFString* stdOutput, CFString* errorOutput)
{
	int std_pipe[2], err_pipe[2];
	int pid;
	int status;
    char* *argsRW;
    int i, argLen;

	if(stdOutput){
		cf_string_safe_cp(stdOutput, "");
		pipe(std_pipe);
	}
	if(errorOutput){
		cf_string_safe_cp(stdOutput, "");
		pipe(err_pipe);
	}
	pid = fork();
	if(pid == 0){
		if(stdOutput){
			close(std_pipe[0]);
			dup2(std_pipe[1], 1);
		}
		if(errorOutput){
			close(err_pipe[0]);
			dup2(err_pipe[1], 2);
		}
        for(i=0; ;i++){
            if(!args[i]){
                argLen = i;
                break;
            }
        }
        argsRW = (char**)malloc((argLen+1)*sizeof(char*));
        for(i=0; i<argLen; i++){
            argsRW[i] = (char*)malloc(strlen(args[i]));
            strcpy(argsRW[i], args[i]);
        }
        argsRW[i]=NULL;
        LCF_DBG("argLen=%d\n", argLen);
		execvp(argsRW[0], argsRW);
        for(i=0; i<argLen; i++){
            free(argsRW[i]);
        }
        free(argsRW);
		exit(-1);
	}else if(pid > 0){
		char std_buf[100]={0}, err_buf[100]={0};
		int std_readed, err_readed;
		if(stdOutput){
			close(std_pipe[1]);
			std_readed = read(std_pipe[0], std_buf, 99);
		}
		if(errorOutput){
			close(err_pipe[1]);
			err_readed = read(err_pipe[0], err_buf, 99);
		}
		while((std_readed&&stdOutput) || (errorOutput&&err_readed))
		{
				if(stdOutput){
					cf_string_safe_cat_n(stdOutput, std_buf, std_readed);
					std_readed = read(std_pipe[0], std_buf, 99);
				}
				if(errorOutput){
					cf_string_safe_cat_n(errorOutput, err_buf, err_readed);
					err_readed = read(err_pipe[0], err_buf, 99);
				}
		}
		waitpid(pid, &status, 0);
		if(stdOutput){
			close(std_pipe[0]);
		}
		if(errorOutput){
			close(err_pipe[0]);
		}
		if(WIFEXITED(status)){
			return WEXITSTATUS(status);
		}else
			return -1;
	}else{
		perror("fork failed\n");
	}
	return -1;
}

int cf_system2(CFString* stdOut, CFString*errOut, const char* arg1, ...)
{
	int n=0, i, res;
	const char* *args;
	va_list ap;
	va_start(ap, arg1);
	while(1){
		if(!va_arg(ap, char*)) break;
		n++;
	}
	va_end(ap);
	args = (const char**)malloc(sizeof(const char*)*(n+2));
	*args = arg1;
	va_start(ap, arg1);
	for(i=1; i <= n+1; i++){
		*(args + i) = va_arg(ap, char*);
	}
	va_end(ap);
	res = cf_system(args, stdOut, errOut);
	free(args);
	return res;
}

#define SHM_KEY_BASE 7000

void* cf_create_shm(const char* key_path, size_t size)
{
	key_t key;
	int shmid = -1;
	void *ret;

	if(size%getpagesize() != 0)	size = (size/getpagesize()+1)*getpagesize();

	if(key_path){
		if(access(key_path, F_OK)) cf_string_to_file(key_path, "");
		key = ftok(key_path, 0);
	}else{
		key = SHM_KEY_BASE;
	}
	if(key != SHM_KEY_BASE){
		shmid = shmget(key, size, IPC_CREAT|IPC_EXCL|0644);
		if(shmid == -1) key = SHM_KEY_BASE;
	}
	if(shmid == -1){
		while(1){
			shmid = shmget(key, size, IPC_CREAT|IPC_EXCL|0644);
			if(shmid != -1) break;
			if(errno == EEXIST) key++;
			else break;
		}
	}
	//printf("set_key=%d\n", key);
	ret = shmat(shmid, NULL, 0);
	if((int)ret == -1){
		perror(NULL);
		shmctl(shmid, IPC_RMID, NULL);
		return NULL;
	}else{
		char keyStr[25];
		sprintf(keyStr, "%lld %llu", (long long)key, (unsigned long long)size);
		if(cf_string_to_file(key_path, keyStr) == -1){
			shmctl(shmid, IPC_RMID, NULL);
			return NULL;
		}
		return ret;
	}
}

static int cf_get_shm_key_size(const char* key_path, key_t* key, size_t* size)
{
	FILE* fp;
	long long int _key;
	unsigned long long int _size;
	char keyStr[50];
	fp = fopen(key_path, "r");
	if(fp == NULL) return -1;
	fgets(keyStr, 25, fp);
	fclose(fp);
	if(sscanf(keyStr, "%lld %llu", &_key, &_size) != 2)
		return -1;
	*key = _key;
	*size = _size;
	return 0;
}


int cf_delete_shm(const char* key_path)
{
	int ret;
	size_t size;
	key_t key;
	if(cf_get_shm_key_size(key_path, &key, &size) == -1) return -1;
	int shmid = shmget(key, size, 0);
	if(shmid == -1) return -1;
	ret = shmctl(shmid, IPC_RMID, NULL);
	if(ret == -1) return -1;
	else{
		unlink(key_path);
		return 0;
	}
}

void* cf_get_shm(const char* key_path)
{
	void *ret;
	int shmid;
	size_t size;
	//if(size%getpagesize() != 0)	size = (size/getpagesize()+1)*getpagesize();
	key_t key;
	if(cf_get_shm_key_size(key_path, &key, &size) == -1) return NULL;
	//printf("get_key=%d\n", key);
	shmid = shmget(key, 1, 0);
	if(shmid == -1) return NULL;
	ret = shmat(shmid, NULL, 0);
	if((int)ret == -1){
		perror(NULL);
		return NULL;
	}
	return ret;
}

void cf_mkdir(const char* dir, mode_t mode)
{
	char *path =  (char*)malloc(strlen(dir)+1);
	strcpy(path, dir);
	char *last_start, *last_end;
	last_start = path;
	last_end = strchr(last_start, '/');
	while(last_end){
		*last_end = '\0';
		mkdir(path, mode);
		*last_end = '/';
		last_start = last_end + 1;
		last_end = strchr(last_start, '/');
	}
	mkdir(path, mode);
	free(path);
}

int cf_try_flock(int fd, int l_type, int base_time, int try_times)
{
	int i = 0;
	while(1){
		i++;
		if(!fcntl(fd, F_SETLK, l_type)) return 0;
		if(i == try_times) break;
		sleep(base_time*i);
	}
	return -1;
}

#define UUID_START_FLAG "UUID=\""
#define UUID_END_FLAG "\""
int cf_get_disk_uuid(const char* dev, CFString* uuid_out)
{
	CFString* output = cf_string_new("");
	int res = -1;
	char *start, *end;
	const char* args[] ={
		"blkid",
		dev,
		NULL
	};
	if(cf_system(args, output, NULL)){
		perror("execute blkid failed");
		goto out;
	}
	if(!strlen(output->str)){
		perror("Got nothing from blkid");
		goto out;
	}
	if(!(start = strstr(output->str, UUID_START_FLAG))){
		perror("parse output from blkid failed");
		goto out;
	}
	start += (sizeof(UUID_START_FLAG) - 1);
	if(!(end = strstr(start, UUID_END_FLAG))){
		perror("parse output from blkid failed");
		goto out;
	}
	end --;
	cf_string_safe_cp_n(uuid_out, start, end-start+1);
	res = 0;
out:
	cf_string_free(output);
	return res;
}

int cf_is_process_alive(const char* pid_file, const char* proc_path)
{
	int res = TRUE;
	CFString *pid_string;
	CFString *maps_file_string;
	struct stat st;
	pid_string = cf_string_new("");
	if(-1 == cf_file_to_string(pid_string, pid_file)){
		res = FALSE;
		goto out0;
	}
	maps_file_string = cf_string_new(proc_path);
	cf_string_safe_cat(maps_file_string, "/");
	cf_string_safe_cat(maps_file_string, cf_string_get_str(pid_string));
	cf_string_safe_cat(maps_file_string, "/maps");
	if(-1 == stat(cf_string_get_str(maps_file_string), &st) && (errno == ENOENT || errno == ENOTDIR)){
		res = FALSE;
	}
	cf_string_free(maps_file_string);
out0:
	cf_string_free(pid_string);
	return res;
}

int cf_timeout_read(int fd, void* buf, size_t nbyte, struct timeval* timeout)
{
	int sel_res;
	fd_set read_fds;
	FD_ZERO(&read_fds);
	FD_SET(fd, &read_fds);
	sel_res = select(fd+1, &read_fds, NULL, NULL, timeout);
	if(sel_res == 0){
		LCF_MSG("waiting for read timeout\n");
		return -1;
	}else if(sel_res < 0 ){
		LCF_ERR("waiting for read error");
		return -1;
	}else if(sel_res == 1 && FD_ISSET(fd, &read_fds)){
		return read(fd, buf, nbyte);
	}else{
		return -1;
	}
}

int cf_timeout_write(int fd, const void* buf, size_t nbyte, struct timeval* timeout )
{
	int sel_res;
	fd_set write_fds;
	FD_ZERO(&write_fds);
	FD_SET(fd, &write_fds);
	sel_res = select(fd+1, NULL, &write_fds, NULL, timeout);
	if(sel_res == 0){
		LCF_MSG("waiting for write timeout\n");
		return -1;
	}else if(sel_res < 0 ){
		LCF_ERR("waiting for write error:%s", strerror(errno));
		return -1;
	}else if(sel_res == 1 && FD_ISSET(fd, &write_fds)){
		return write(fd, buf, nbyte);
	}else{
		return -1;
	}
}

int cf_receive_msg(int fd, CFString* bufStr, const char* endFlag, size_t max_rec_len, struct timeval* timeout)
{
	size_t readed = 0;
	int read_res;
	char buf[4];
	size_t end_flag_len = strlen(endFlag);
	cf_string_safe_cp(bufStr, "");
	while(1){
		read_res = cf_timeout_read(fd, buf, sizeof(buf), timeout);
		if(read_res == -1){
			LCF_ERR("Receive data error\n");
			return -1;
		}else if(read_res > 0){
			readed += read_res;
			cf_string_safe_cat_n(bufStr, buf, read_res);
			if(readed >= end_flag_len
				&& 0 == memcmp(cf_string_get_str(bufStr)+(readed-end_flag_len),
								endFlag, end_flag_len)){
				return readed;
			}
			if(readed > max_rec_len){
				LCF_ERR("Message is too long\n");
				return -1;
			}
		}else if(read_res == 0){
			return 0;
		}
	}

}

int cf_send(int fd, const void* buf, size_t nbyte, struct timeval* timeout)
{
	size_t writed = 0;
	int write_res;
	while(1){
		write_res = cf_timeout_write(fd, buf+writed, nbyte-writed, timeout);
		if(write_res == -1){
			LCF_ERR("Send data error\n");
			return -1;
		}else if(write_res >= 0){
			writed += write_res;
			if(writed == nbyte){
				return nbyte;
			}
		}
	}

}

int flock_timeout(int fd, int operation, int timeout_sec)
{
    while(timeout_sec){
        if(-1 == flock(fd, operation)){
            timeout_sec--;
            sleep(1);
        }else{
            break;
        }
    }
    if(!timeout_sec && -1 == flock(fd, operation)){
        return -1;
    }
    return 0;
}


int cf_get_process_path(CFString * path, pid_t pid)
{
#define INIT_PATH_BUF_LEN 1000
    char exe[] = "/proc/1234567890,1234567890,1234567890/exe";
    int i = 0;
    int path_len;
    sprintf(exe, "/proc/%d/exe", pid);
    LCF_DBG("2\n");
    while(1){
        i++;
        char* buf = (char*)malloc(INIT_PATH_BUF_LEN*i);
        path_len = readlink(exe, buf, INIT_PATH_BUF_LEN*i);
     LCF_DBG("3\n");
        if(path_len == -1){
            free(buf);
            return -1;
        }else if(path_len < INIT_PATH_BUF_LEN*i){
            cf_string_safe_cp_n(path, buf, path_len);
            free(buf);
            return 0;
        }
        free(buf);
    }
}

int cf_get_process_dir(CFString* dir, pid_t pid)
{
    CFString* path = cf_string_new("");
    int res;
    if(0 == cf_get_process_path(path, pid)){
        LCF_DBG("4%s\n", cf_string_get_str(path));
        res = cf_file_to_dir(dir, cf_string_get_str(path));
        LCF_DBG("5\n");
    }else{
        res = -1;
    }
    cf_string_free(path);
    return res;
}
#endif
int cf_string_to_file(const char* filePath, const char* str)
{
	FILE *fp;
	fp = fopen(filePath, "w");
	if(!fp) return -1;
	fputs(str, fp);
	fclose(fp);
	return 0;
}

int cf_fd_to_string(CFString* str, int fd, int len)
{
	char buf[512];
    int needRead = len;

    CFStringSafeCp(str, "");
    if(len == -1)
        needRead = sizeof(buf);
	while(1){
        int readed = read(fd, buf, needRead<sizeof(buf)?needRead:sizeof(buf));
        if(len == -1)
            needRead = sizeof(buf);
        else
            needRead = needRead - readed;
        if(readed>0){
            cf_string_safe_cat_n(str, buf, readed);
        }
        if(!readed || !needRead) break;
    }
	return 0;
}

int cf_file_to_string(CFString* str, const char* filePath)
{
	struct stat st;
	int fd;
	if(stat(filePath, &st)){
		LCF_ERR("Can not find %s\n", filePath);
		return -1;
	}
	fd = open(filePath, O_RDONLY);
	if(fd == -1){
		LCF_ERR("Open %s failed:", filePath);
		perror(NULL);
		return -1;
	}
	cf_fd_to_string(str, fd, -1);
	close(fd);
	return 0;
}

int CFFdToInt(int* val, int fd)
{
    char buf[10];
    int readed;
    
    if((readed=read(fd, buf, sizeof(buf)))<0){
        LCF_ERR("Read failed\n");
        return -1;
    }
    buf[readed] = 0;
    if(1!=sscanf(buf, "%d", val)){
        return -1;
    }
    return 0;
}

int CFFileToInt(int* val, const char* filePath)
{
	struct stat st;
	int fd;
    
	if(stat(filePath, &st)){
		LCF_SYS_ERR_OUT(ERR_OUT, "stat %s failed\n", filePath);
		return -1;
	}
	fd = open(filePath, O_RDONLY);
	if(fd == -1){
		LCF_SYS_ERR_OUT(ERR_OUT, "Open %s failed\n", filePath);
	}
	if(CFFdToInt(val, fd)){
        LCF_ERR_OUT(ERR_CLOSE_FD, "\n");
    }
	close(fd);
	return 0;
ERR_CLOSE_FD:
    close(fd);
ERR_OUT:
    return -1;
}

int cf_file_to_dir(CFString* dir, const char* file)
{
    char* slash;
    slash = strrchr(file, '/');
    if(slash){
        cf_string_safe_cp_n(dir, file, slash-file);
    }else{
        cf_string_safe_cp_n(dir, ".", 1);
    }
    return 0;
}


void CFHalfByteToHex(char hex[1], const char halfByte)
{
    if(halfByte<0xa){
        hex[0] = halfByte+48;
    }else{
        hex[0] = halfByte+55;
    }
}

void CFByteToHex(char hex[2], const char byte)
{
    CFHalfByteToHex(hex, (byte&0xF0)>>4);
    CFHalfByteToHex(hex+1, byte&0x0F);
}

int CFRawToHex(char* string, void* raw, size_t rawSize)
{
    int i;

    for(i=0; i<rawSize; i++){
        CFByteToHex(&string[2*i], ((unsigned char*)raw)[i]);
    }
    return 0;
}

int CFFdPrintf(int fd, const char *fmt, ...)
{
    FILE* out;
    va_list ap;
    int ret;

    if(!(out = fdopen(fd, "w"))){
        LCF_ERR("\n");
        return -1;
    }
    va_start(ap, fmt);
    ret = vfprintf(out, fmt, ap);
    va_end(ap);
    return ret;
}

int CFToggleProcessAlive(int lockPort)
{
    int sock_fd;
    struct sockaddr_in addr;
    
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_fd == -1){
        LCF_ERR("Can not initial unix socket for cmd interface\n");
        return -1;
    }
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(lockPort);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(-1 == bind(sock_fd, (struct sockaddr* )&addr, sizeof(struct sockaddr_in)) || -1==listen(sock_fd, 1)){
        LCF_ERR("bind or listen socket failed: %s\n", strerror(errno));
        close(sock_fd);
        return -1;
    }
    return 0;    
}

BOOL CFIsProcessAlive(int lockPort)
{
    int sock_fd;
    int ret;
    struct sockaddr_in addr;
    
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_fd == -1){
        LCF_ERR("Can not initial unix socket for cmd interface\n");
        return -1;
    }
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(lockPort);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(-1 == bind(sock_fd, (struct sockaddr* )&addr, sizeof(struct sockaddr_in)) || -1==listen(sock_fd, 1)){
        LCF_ERR("bind unix socket for cmd interface failed: %s\n", strerror(errno));
        ret = TRUE;
    }else{
        ret = FALSE;
    }
    close(sock_fd);
    return ret;
}

static void __dummyIntProcess(int fd){}

int CFDaemon(const char* pidFile, int lockPort)
{
    int pid;
    int st;
    struct sigaction action;

    if(CFToggleProcessAlive(lockPort)){
        CFErrno = CF_ERR_RUNNING;
        return -1;
    }
    if(CFIntToFile(pidFile, getpid())){
        LCF_ERR("\n");
        return -1;
    }
    memset(&action, 0, sizeof(action));
    action.sa_handler = __dummyIntProcess;
    action.sa_flags = 0;
    /*action.sa_sigaction = NULL;*/ /* The storage occupied by sa_handler and sa_sigaction may overlap */
    sigemptyset(&action.sa_mask);
    
    if(sigaction(SIGINT, &action, NULL)){
        LCF_ERR("\n");
        return -1;
    }

    while(1){
        pid = fork();
        if(pid<0){
            LCF_ERR("\n");
            return -1;
        }else if(pid>0){
            if(-1 == waitpid(pid, &st, 0)){
                if(errno == EINTR){
                    kill(pid, SIGINT);
                    waitpid(pid, &st, 0);
                    CFErrno = CF_ERR_FORCE_EXTI;
                    goto ERR_OUT;
                }else{
                    CFErrno = CF_ERR_UNKNOWN;
                    goto ERR_OUT;
                }
            }
        }else{
            break;
        }
    }
    setsid();
    umask(0);
    return 0;
ERR_OUT:
    remove(pidFile);
    return -1;
}

int CFStopDaemon(const char* pidFile)
{
    pid_t pid;
    if(CFFileToInt(&pid, pidFile)){
        return -1;
    }
    if(kill(pid, SIGINT)){
        LCF_SYS_ERR("\n");
        return -1;
    }
    return 0;
}

BOOL CFIsDaemonExited(int lockPort)
{
    return CFIsProcessAlive(lockPort);
}

/* Filesystem utilities */
int CFFileIsCharDevice(const char* filePath)
{
    struct stat st;
    if(-1 == stat(filePath, &st)){
        LCF_ERR("stat failed: %s\n", strerror(errno));
        return FALSE;
    }
    return S_ISCHR(st.st_mode);
}

